package com.jhf.youke.saga.client.domain.service;


import cn.hutool.json.JSONUtil;
import com.jhf.youke.core.ddd.DomainMq;
import com.jhf.youke.core.entity.Message;
import com.jhf.youke.core.entity.RedisHashItemDto;
import com.jhf.youke.core.entity.SendResult;
import com.jhf.youke.core.utils.CacheUtils;
import com.jhf.youke.core.utils.Constant;
import com.jhf.youke.saga.client.domain.converter.SagaConverter;
import com.jhf.youke.saga.client.domain.converter.SagaMessageConverter;
import com.jhf.youke.saga.client.domain.gateway.SagaMessageRepository;
import com.jhf.youke.saga.client.domain.gateway.SagaRepository;
import com.jhf.youke.saga.client.domain.model.Do.SagaDo;
import com.jhf.youke.saga.client.domain.model.Do.SagaMessageDo;
import com.jhf.youke.saga.client.domain.model.po.SagaMessagePo;
import com.jhf.youke.saga.client.domain.model.po.SagaPo;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

/**
 * @author RHJ
 * **/
@Log4j2
@Component
public class SagaClientService {


    @Value("${feign.mqUrl}")
    public String mqUrl;

    @Resource
    private SagaRepository repository;

    @Resource
    private SagaMessageRepository sagaMessageRepository;

    @Resource
    private SagaMessageConverter sagaMessageConverter;

    @Resource
    private SagaConverter sagaConverter;

    @Resource
    private DomainMq domainMq;

    /**
    * @Description:  根据业务对象、code、sort更新saga
    * @Param: [saga]
    * @return: void
    * @Author: RHJ
    * @Date: 2022/3/8
    */
    public void updateByBiz(SagaDo saga){
        SagaPo sagaPo = sagaConverter.do2Po(saga);
        repository.updateByBiz(sagaPo);
    }

    public SagaDo getByBiz(String code, Long bizId, Integer objectId, Integer sort){
        SagaPo res = repository.getByBiz(code,bizId,objectId,sort);
        SagaDo sagaDo = sagaConverter.po2Do(res);
        return sagaDo;
    }

    /**
    * @Description:  接收后，创建saga
    * @Param: [map]
    * @return: void
    * @Author: RHJ
    * @Date: 2022/3/8
    */
    public SagaDo create(Map<String, Object> map) {
        SagaDo saga = new SagaDo(map);
        SagaDo old = getByBiz(saga.getCode(),saga.getBizId(),saga.getObjectId(),saga.getSort());
        if(old != null){
            return old;
        }else{
            saga.setStatus(SagaDo.RECEIVE_OK);
            SagaPo sagaPo = sagaConverter.do2Po(saga);
            sagaPo.preInsert();;
            repository.insert(sagaPo);
        }

        return saga;
    }

    /**
    * @Description:  将业务消息存数据数据库与redis
    * @Param: [map]
    * @return: java.util.Map<java.lang.String,java.lang.Object>
    * @Author: RHJ
    * @Date: 2022/3/14
    */
    public SagaMessageDo createMsg(Map<String, Object> map)throws Exception{
        SagaMessageDo sagaMessage = new SagaMessageDo(map);
        String key = "saga_msg_"+ sagaMessage.getCode() +"_"+sagaMessage.getBizId();
        CacheUtils.set(key, sagaMessage.getMessage(), 20 * 60 *60);
        SagaMessagePo oldMsg = sagaMessageRepository.getByBiz(sagaMessage.getCode(),sagaMessage.getBizId(),sagaMessage.getObjectId());
        if(oldMsg != null){
            return sagaMessageConverter.po2Do(oldMsg);
        }else{
            SagaMessagePo sagaMessagePo = sagaMessageConverter.do2Po(sagaMessage);
            sagaMessagePo.preInsert();
            sagaMessageRepository.insert(sagaMessagePo);
            return sagaMessage;
        }

    }

    /**
    * @Description:  应答成功， 更新saga,并发送MQ
    * @Param: [map]
    * @return: void
    * @Author: RHJ
    * @Date: 2022/3/8
    */
    public void reply(SagaDo saga, String abnormalKey){
       sendMq(saga, abnormalKey);
    }

    public void replyFail(SagaDo saga,String abnormalKey){
        saga.setStatus(SagaDo.CONSUME_SEND_FAIL);
        updateByBiz(saga);
        saveAbnormalToRedis(saga, abnormalKey);
    }

    /**
    * @Description:  业务消费失败
    * @Param: [map]
    * @return: void
    * @Author: RHJ
    * @Date: 2022/3/8
    */
    public void fail(SagaDo saga, String abnormalKey){
        saga.setStatus(SagaDo.CONSUME_FAIL);
        updateByBiz(saga);
        saveAbnormalToRedis(saga, abnormalKey);
    }

    /**
    * @Description:  消费成功
    * @Param: [map]
    * @return: void
    * @Author: RHJ
    * @Date: 2022/3/14
    */
    public void ok(SagaDo saga){
        saga.setStatus(SagaDo.CONSUME_OK);
        updateByBiz(saga);
    }

    /**
    * @Description: 将应答消息发送MQ
    * @Param: [saga]
    * @return: java.lang.String
    * @Author: RHJ
    * @Date: 2022/3/8
    */
    public void sendMq(SagaDo saga, String abnormalKey){
        Message message = new Message();
        message.setTopic(SagaDo.SAGA_REPLY_CONSUME);
        message.setTag("all");
        message.setGroupName("sagaConsumeGroup");

        message.setMessages(getMsgInfo(saga, getMsg(saga)));
        SendResult result = domainMq.send(JSONUtil.toJsonStr(message));
        // 发送成功，再更新saga状态
        if(Constant.RESPONSE_OK.equals(result.getCode())){
            log.info("mq messageId {}", result.getData().getMsgId());
            saga.setMessageId(result.getData().getMsgId());
            saga.setStatus(SagaDo.CONSUME_SEND_OK);
        }else{
            saga.setStatus(SagaDo.CONSUME_SEND_FAIL);
            saveAbnormalToRedis(saga,abnormalKey);

        }
        updateByBiz(saga);


    }

    /**
    * @Description:  拿到saga消息
    * @Param: [saga]
    * @return: java.util.Map<java.lang.String,java.lang.Object>
    * @Author: RHJ
    * @Date: 2022/4/24
    */
    public Map<String,Object> getMsgInfo(SagaDo saga, Map<String,Object> message){
        Map<String,Object> msg = new HashMap<>(16);
        msg.put("sort", saga.getSort());
        msg.put("code", saga.getCode());
        msg.put("topic",saga.getTopic());
        msg.put("bizId",saga.getBizId());
        msg.put("objectId",saga.getObjectId());
        msg.put("status",saga.getStatus());
        msg.put("ifCurrent",saga.getIfCurrent());
        msg.put("ifEnd", saga.getIfEnd());
        msg.put("message", message);
        return msg;
    }

    /**
     * @Description: 将异常信息放入redis中
     * @Param: [saga]
     * @return: ibm.bp.emsp.entity.RedisHashDto
     * @Author: RHJ
     * @Date: 2022/3/5
     */
    public void saveAbnormalToRedis(SagaDo saga, String abnormalKey){
        RedisHashItemDto dto = new RedisHashItemDto();
        dto.setKey(abnormalKey);
        dto.setItem(saga.getId().toString());
        dto.setValue(saga);
        dto.setTime(20*60*60);
        CacheUtils.setHashItem(dto);
    }


    /**
    * @Description:  拿到业务的Map
    * @Param: [saga]
    * @return: java.util.Map<java.lang.String,java.lang.Object>
    * @Author: RHJ
    * @Date: 2022/3/14
    */
    public Map<String,Object> getMsg(SagaDo saga) {
        String key = "saga_msg_"+ saga.getCode() +"_"+saga.getBizId();
        Map<String,Object> map = null;
        try {
            map =CacheUtils.getObject(key, Map.class);
        }catch (Exception e){
            log.info(e.getMessage());
        }

        if(map == null || map.isEmpty()){
            SagaMessagePo message = sagaMessageRepository.getByBiz(saga.getCode(),saga.getBizId(),saga.getObjectId());
            if(message !=null && message.getMessage() !=null){
                CacheUtils.set(key, message.getMessage(), 20*60*60);
                String m = message.getMessage();
                map = JSONUtil.toBean(m, Map.class);
                return map;
            }else{
                return new HashMap<>(8);
            }
        }
        return map;
    }


    public String sendMq(SagaDo saga, Object msg){
        Message message = new Message();
        message.setTopic(saga.getTopic());
        message.setTag("all");
        message.setGroupName("sagaGroup");
        Map<String,Object> map = new HashMap<>(16);
        map.put("sort", saga.getSort());
        map.put("code", saga.getCode());
        map.put("topic",saga.getTopic());
        map.put("bizId",saga.getBizId());
        map.put("objectId",saga.getObjectId());
        map.put("status",saga.getStatus());
        map.put("ifCurrent",saga.getIfCurrent());
        map.put("messageId",saga.getMessageId());
        map.put("ifEnd", saga.getIfEnd());
        map.put("message", msg);
        message.setMessages(map);
        return JSONUtil.toJsonStr(message);
    }


    public static void main(String[] args) throws Exception{

    }


}

